package com.xinyue.server.framework.server.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.xinyue.game.utils.encrypt.AESUtils;
import com.xinyue.game.utils.encrypt.GzipUtil;
import com.xinyue.server.framework.handlermapping.GameHandlerMappingManager;
import com.xinyue.server.framework.handlermapping.IGameMessage;
import com.xinyue.server.framework.handlermapping.MessageClassKey;
import com.xinyue.server.framework.message.GameMessageHeader;
import com.xinyue.server.framework.message.GameMessagePackage;
import com.xinyue.server.framework.message.GameMessageType;
import com.xinyue.server.framework.server.EncodeResult;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;

public class GameMessageCodecService {
    private static Logger logger = LoggerFactory.getLogger(GameMessageCodecService.class);
    // 包头固定占用的字节数
    private static final int MESSAGE_FIX_SIZE = 27;

    public static ByteBuf encode(GameMessagePackage response, byte[] aesKey) throws Exception {
        int totalSize = MESSAGE_FIX_SIZE;
        GameMessageHeader header = response.getHeader();
        // 如果错误码不为0，需要加入错误描述信息的长度
        // 处理错误码,如果错误码不为0，需要添加错误信息
        byte[] errorMsgBytes = null;
        if (header.getErrorCode() != 0) {
            if (header.getErrorMsg() == null) {
                header.setErrorMsg("");
            }
            errorMsgBytes = header.getErrorMsg().getBytes(StandardCharsets.UTF_8);
            totalSize += errorMsgBytes.length;
        }
        IGameMessage body = response.getBody();
        // 将消息体的长度也计算出来
        EncodeResult encodeResult = encodeBody(response);
        byte[] bodyBytes = encodeResult.getBodyBytes();
        if (bodyBytes != null) {
            bodyBytes = encryptAndCompress(body, bodyBytes, aesKey);
            if (bodyBytes != null) {
                totalSize += bodyBytes.length;
            }
        }
        // 创建网络缓冲对象
        ByteBuf byteBuf = Unpooled.buffer(totalSize);
        // 写入消息包总长度值,占4byte
        byteBuf.writeInt(totalSize);
        //写入消息包的公共信息,占8byte
        byteBuf.writeInt(header.getRequestId());
        // 逻辑消息id,占4位
        byteBuf.writeInt(header.getLogicMessageId());
        // 消息类型，占1byte
        byteBuf.writeByte(header.getMessageType().getType());
        // 返回时间，占8byte
        byteBuf.writeLong(header.getCreateTime());

        // 错误码，占4byte
        byteBuf.writeInt(header.getErrorCode());
        // 处理错误码,如果错误码不为0，需要添加错误信息
        if (errorMsgBytes != null) {
            byteBuf.writeShort(errorMsgBytes.length);
            byteBuf.writeBytes(errorMsgBytes);
        }

        //写入包体信息
        if (bodyBytes != null) {
            // 是否需要压缩，占1byte
            byteBuf.writeByte((body.checkNeedCompress() ? 1 : 0));
            // 是否需要加密，占1byte
            byteBuf.writeByte(body.checkNeedEncrypt() ? 1 : 0);
            byteBuf.writeBytes(bodyBytes);
        }
        header.setMessageSize(totalSize);
        if (logger.isDebugEnabled()) {
            logger.debug("response<--header={},body={}", JSON.toJSONString(header), encodeResult.getBodyStr());
        }
        return byteBuf;
    }

    public static GameMessagePackage decode(ByteBuf byteBuf, byte[] aesKey) throws Exception {
        int messageSize = 0;
        int logicMessageId = 0;
        // 读取消息，这没办法，只能一个一个读取了
        messageSize = byteBuf.readInt();
        int requestId = byteBuf.readInt();
        logicMessageId = byteBuf.readInt();
        long requestTime = byteBuf.readLong();

        byte[] body = readBodyBytes(aesKey, byteBuf);
        GameMessageHeader header = new GameMessageHeader();
        header.setMessageSize(messageSize);
        header.setRequestId(requestId);
        header.setMessageType(GameMessageType.REQUEST);
        header.setLogicMessageId(logicMessageId);
        header.setCreateTime(requestTime);
        GameMessagePackage gameMessagePackage = createGameMessagePackage(header, body);
        return gameMessagePackage;
    }

    private static byte[] readBodyBytes(byte[] aesKey, ByteBuf byteBuf) throws Exception {
        byte[] body = null;
        if (byteBuf.isReadable()) {
            boolean isEncrypt = byteBuf.readByte() == 1 ? true : false;
            boolean isUncompress = byteBuf.readByte() == 1 ? true : false;
            body = new byte[byteBuf.readableBytes()];
            byteBuf.readBytes(body);
            if (isEncrypt) {
                // 此消息经过加密了，读取的时候，需要解密
                if (aesKey != null) {
                    body = AESUtils.decode(aesKey, body);
                } else {
                    logger.debug("对称密钥为空，不执行解密操作");
                }
            }
            if (isUncompress) {
                // 说明此消息压缩了，需要解压
                body = GzipUtil.uncompress(body);
            }
        }
        return body;
    }

    private static GameMessagePackage createGameMessagePackage(GameMessageHeader header, byte[] bodyBuf) {
        GameMessagePackage gameMessagePackage = new GameMessagePackage();
        gameMessagePackage.setHeader(header);
        IGameMessage gameMessage = decodeGameMessage(header, bodyBuf);
        gameMessagePackage.setBody(gameMessage);
        return gameMessagePackage;
    }

    private static IGameMessage decodeGameMessage(GameMessageHeader header, byte[] bodyBuf) {

        GameHandlerMappingManager gameHandlerMappingManager = GameHandlerMappingManager.getInstance();
        int messageId = header.getLogicMessageId();
        int messageType = GameMessageType.REQUEST.getType();
        MessageClassKey classKey = new MessageClassKey(messageId, messageType);
        IGameMessage gameMessage = null;
        String body = null;
        if (bodyBuf != null) {
            body = new String(bodyBuf, StandardCharsets.UTF_8);
            Class<? extends IGameMessage> clazz = gameHandlerMappingManager.getRequestClass(classKey);
            try {
                gameMessage = JSON.parseObject(body, clazz);
            } catch (Exception e) {
                logger.error("客户端数据不合法,json = {}", body);
                throw e;
            }
        }
        logger.debug("request-->header={},body={}", header, body);
        return gameMessage;
    }

    private static EncodeResult encodeBody(GameMessagePackage gameMessagePackage) {
        byte[] bodyBytes = null;
        String body = null;
        EncodeResult encodeResult = new EncodeResult();
        if (gameMessagePackage.getBody() != null) {
            body = JSON.toJSONString(gameMessagePackage.getBody(), SerializerFeature.DisableCircularReferenceDetect);
            bodyBytes = body.getBytes(StandardCharsets.UTF_8);
            encodeResult.setBodyBytes(bodyBytes);
            encodeResult.setBodyStr(body);
        }
        return encodeResult;
    }

    private static byte[] encryptAndCompress(IGameMessage body, byte[] bodyBytes, byte[] aesKey) throws Exception {
        if (bodyBytes != null) {
            //判断是否需要压缩
            if (body.checkNeedCompress()) {
                bodyBytes = GzipUtil.compress(bodyBytes);
            }
            //判断是否需要加密
            if (body.checkNeedEncrypt()) {
                if (aesKey != null) {
                    bodyBytes = AESUtils.encode(aesKey, bodyBytes);
                }
            }
        }
        return bodyBytes;
    }
}
